#define znew  ((z=36969*(z&65535)+(z>>16))<<16)
#define wnew  ((w=18000*(w&65535)+(w>>16))&65535)
#define IUNI  (znew+wnew)
#define UNI   (znew+wnew)*2.328306e-10
#include "base/object.h"
#include "base/triangle.h"
#include <list>
static unsigned long z=362436069, w=521288629;
inline void setseed(unsigned long i1,unsigned long i2)
{
        z=i1;
        w=i2;
}

inline double Noise2(int x, int y)
{

}

inline double Noise3(int x, int y)
{


//return (Noise2(x, y)*2+UNI)/3.0;

        //return Noise2(x, y);
        int n=(int)x*331+(int)y*337;

        n=(n<<13)^n;

        int nn=(n*(n*n*41333 +53307781)+1376312589)&0x7fffffff;

        return ((1.0-((double)nn/1073741824.0))+1)/2.0;


}

inline double CosineInterpolate(double a, double b, double x )
{
        //
        double ft = x * 3.1415927;
        double f = ((1.0 - cos(ft)) * 0.5);

        return  (a*(1.0-f) + b*f);


}


inline double Smooth(int i, int j)
{



        //return (double)(Array[i][j]+Array[i+1][j]+Array[i][j+1]+Array[i+1][j+1])/4;
        //return (double)(Noise3(i, j)+Noise3(i+1, j)+Noise3(i, j+1)+Noise3(i+1, j+1)+Noise3(i-1,j-1)+Noise3(i-1,j)+Noise3())/5.0;
        return double(Noise3(i,j)+Noise3(i-1, j)+Noise3(i+1, j)+Noise3(i,j+1)+Noise3(i,j-1))/5.0;

}


double ** pmap(int width, int height, int freq, double Zoom)
{

        double **map=new double*[width];
        for(int i=0; i<width; i++) {

                map[i]=new double[height];

        }


        for(int i=0; i<width; i++) {
                for(int j=0; j<height; j++) {

                        //setseed(i*freq, j*freq);

                        //map[i][j]=UNI;
                        //map[i][j]=Smooth((i*freq)/Zoom, (j*freq)/Zoom);


                        double X=((i*freq)/Zoom);
                        double Y=((j*freq)/Zoom);

                        double Xt, Yt;

                        Xt = (2*i)/(((width*2)-1))-1;
                        Yt = (2*j)/(((width*2)-1))-1;

                        //fisheye(Xt, Yt, temp, temp1);




                        double V1 = Smooth(X, Y);

                        double V2= Smooth(X+1, Y);

                        double V3=Smooth(X, Y+1);

                        double V4=Smooth(X+1,Y+1);


                        double I1=CosineInterpolate(V1, V2, X-int(X));

                        double I2=CosineInterpolate(V3, V4, X-int(X));

                        map[i][j]=CosineInterpolate(I1, I2,Y-int(Y));

                        //map[i][j]=(cos(map[i][j])+1)/2.0;




                }

        }

        return map;

}


double ** addmaps(int width, int height, int octaves, double Zoom, double mult)
{

        double ***maps=new double**[octaves];


        double **map=new double*[width];



        for(int i=0; i<width; i++) {
                map[i]=new double[height];
                for(int j=0; j<height; j++) {
                        map[i][j]=0;
                }
        }



        for(int i=0; i<octaves; i++) {

                maps[i]=pmap(width, height, pow(2, i), Zoom);

        }



        for(int j=0; j<width; j++) {

                for(int n=0; n<height; n++) {

                        for(int i=0; i<octaves; i++) {
                                map[j][n]+=maps[i][j][n];
                        }
                        map[j][n]/=octaves;

                }

        }


        for(int i=0; i<octaves; i++) {
                for(int j=0; j<width; j++) {
                        delete [] maps[i][j];
                }
                delete [] maps[i];

        }

        delete [] maps;

        double **nmap;
        nmap=new double*[width];
        for(int i=0; i<width; i++){
            nmap[i]=new double[height];
        }

        double w2=width/2.0;
        double h2=height/2.0;

        for(int i=0; i<width; i++){
            for(int j=0; j<height; j++){
                ///r = sin(pi/2*r)
                /*double radius=sqrt((i-w2)*(i-w2)+(j-h2)*(j-h2));

                double angle=atan2(j-h2, i-w2);

                //double radius=sqrt(i*i + j*j);
                //double angle = atan2(j, i);


                radius/=sqrt(w2*w2 + h2*h2);
                //std::cout << radius << "\n";
                //radius=sin((3.141592/2.0)*radius);
                radius*=sqrt((i-w2)*(i-w2)+(j-h2)*(j-h2));
                //radius+=width/2.0;

                int ni=round(radius*cos(angle)+w2);
                int nj=round(radius*sin(angle)+h2);

                //ni+=w2;
                //nj+=h2;

                if(ni >= width){
                    ni=width-1;
                }
                if(nj>=height){
                    nj=height-1;
                }*/

                nmap[(int)i][(int)j]=map[i][j]*mult;

                //radius < 0 ? std::cout << radius << "\n", 0 : 0;


            }

        }

        for(int i=0; i<width; i++){
            delete [] map[i];
        }

        delete [] map;




        return nmap;
}


double smoothed_pixel(double i, double j, double freq, double zoom, double octaves){



    double ret=0;


    for(int p=0; p<octaves; p++){


                double ofreq=pow(freq, p);


                double x=((i*ofreq)/zoom)+100;
                double y=((j*ofreq)/zoom)+100;


                double v1=Smooth(x, y);

                double v2=Smooth(x+1, y);

                double v3=Smooth(x, y+1);

                double v4=Smooth(x+1, y+1);


                double i1=CosineInterpolate(v1, v2, x-int(x));

                double i2=CosineInterpolate(v3, v4, x-int(x));




                ret+=CosineInterpolate(i1, i2, y-int(y));


    }


    return ret/octaves;



}

v3d ret_normal(v3d one, v3d two, v3d three)
{
    v3d u=two-one;
    v3d v=three-one;

    v3d normal(u.y*v.z - u.z*v.y, u.z*v.x - u.x*v.z, u.x*v.y - u.y*v.x);

    return normal;
}


///x=r sinu cosv
///y=r cosu cosv
///z=r sinv

void noise_obj(object* obj, int width, int height, int vx, int vy)
{

    std::vector<triangle> tris;
    int mult=100;
    double zoom=24;
    double octaves=3;

    //double **noisemap=addmaps(1024, 1024, octaves, zoom, mult); ///use this as the texture as well ///Hmm. Find nearest poower of two noise

    double **noisemap=new double*[1024];
    for(int i=0; i<1024; i++){
        noisemap[i]=new double[1024];
    }
    for(int i=0; i<1024; i++)
        for(int j=0; j<1024; j++)
            noisemap[i][j]=smoothed_pixel(i, j, 2, zoom, octaves)*mult;

    double xstep=(double)width/(double)vx; //we'll do it live
    double ystep=(double)height/(double)vy;


    std::cout << vx*vy*2;
    obj->gid_take();


    double min=1*mult;
    double max=0;

    for(int i=0; i<1024; i++)
        for(int j=0; j<1024; j++){
            min = noisemap[i][j] < min ? noisemap[i][j] : min;
            max = noisemap[i][j] > max ? noisemap[i][j] : max;

        }

    for(double i=0; i<width-xstep; i+=xstep){
         //std::cout << i << std::endl;
        for(double j=0; j<height-ystep; j+=ystep){

            //std::cout << "j " << j << std::endl;




            //std::cout << noisemap[i][j] << std::endl;
            triangle tri;

            //tri.add_point()

            v3d normal(1,1,10);

            vertex vert[4];
            /*v3d pos(i, j, noisemap[i][j]);
            v3d pos1(i+xstep, j, noisemap[i+xstep][j]);
            v3d pos2(i+xstep, j+ystep, noisemap[i+xstep][j+ystep]);
            v3d pos3(i, j+ystep, noisemap[i][j+ystep]);*/


            ///interpolate z coordinate

            double xdiff=i-(int)i;
            double ydiff=j-(int)j;




            v3d pos[4];


            pos[0]={i,          mult*smoothed_pixel(i, j, 2, zoom, octaves)-min,                  j};
            pos[1]={i+xstep,    mult*smoothed_pixel(i+xstep, j, 2, zoom, octaves)-min,            j};
            pos[2]={i+xstep,    mult*smoothed_pixel(i+xstep, j+ystep, 2, zoom, octaves)-min,      j+ystep};
            pos[3]={i,          mult*smoothed_pixel(i, j+ystep, 2, zoom, octaves)-min,            j+ystep};


            v3d uvws[4];
            for(int i=0; i<4; i++){
                uvws[i].z=obj->gid;
            }
            uvws[0].x=i/(float)1024;
            uvws[0].y=j/(float)1024;

            uvws[1].x=(i+xstep)/(float)1024;
            uvws[1].y=j/(float)1024;

            uvws[2].x=(i+xstep)/(float)1024;
            uvws[2].y=(j+ystep)/(float)1024;

            uvws[3].x=(i)/(float)1024;
            uvws[3].y=(j+ystep)/(float)1024;


            v3d n1, n2;
            n1=ret_normal(pos[0], pos[1], pos[2]);
            n2=ret_normal(pos[0], pos[2], pos[3]);

            v3d tn1;//, tn2;
            tn1=n1+n2;
            for(int i=0; i<4; i++){
                vert[i].pos=pos[i];
                vert[i].normal=normal;
                vert[i].uvw=uvws[i];
                //vert[i].normal=-n1;
            }



            vert[0].normal=tn1/2.0;
            vert[1].normal=n1;
            vert[2].normal=tn1/2.0;
            vert[3].normal=n2;

            tri.add_point(vert[0]);
            tri.add_point(vert[1]);
            tri.add_point(vert[2]);


            tris.push_back(tri);
            tri.pcount=0;
            tri.add_point(vert[0]);
            tri.add_point(vert[2]);
            tri.add_point(vert[3]);
            tris.push_back(tri);

        }
    }

    obj->allocate_triangles(tris.size());



    std::vector<triangle>::iterator it=tris.begin();

    for(int i=0; i<tris.size(); i++, (it)++){

        obj->add_triangle((*it));


    }






    cl_float4* tex;
    tex=new cl_float4[1024*1024];///find nearest power of two


    for(int i=0; i<1024; i++){
        for(int j=0; j<1024; j++){

            noisemap[i][j]-=min;

            noisemap[i][j]/=(max-min);
            noisemap[i][j]=1.0f-noisemap[i][j];
            //std::cout << noisemap[i][j] << std::endl;
        }
    }

    for(int i=0; i<1024; i++){
        for(int j=0; j<1024; j++){

            if(noisemap[i][j] > 0.55){
                tex[i + 1024*j].x=255*noisemap[i][j];
                tex[i + 1024*j].y=255*noisemap[i][j];
                tex[i + 1024*j].z=255*noisemap[i][j];
                //tex[i+1024*j]*=noisemap[i][j];
            }
            else if(noisemap[i][j] < 0.55 && noisemap[i][j] > 0.30){
                tex[i+1024*j].x=40*noisemap[i][j];
                tex[i+1024*j].y=205*noisemap[i][j];
                tex[i+1024*j].z=40*noisemap[i][j];
            }
            else if(noisemap[i][j] < 0.30){
                tex[i + 1024*j].x=0;
                tex[i + 1024*j].y=0;
                tex[i + 1024*j].z=255*noisemap[i][j];
            }

        }
    }

    obj->texture_load(tex, 1024, 1024);

    delete [] tex;
    delete [] noisemap;



}
